package org.tri.model;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.MutableComboBoxModel;

/**
 * La clase {@code EntityComboBoxModel} es un modelo para combo box que se
 * utiliza en conjunto con una lista de entidades, que permite mostrar
 * información de una propiedad en particular del objeto, para que el usuario
 * seleccione luego, y asi retomar el objeto seleccionado para futuro
 * procesamiento.
 *
 * @author kiira
 * @version 1.0, Nov 22, 2011
 */
public class EntityComboBoxModel<K> extends AbstractListModel implements MutableComboBoxModel, Serializable {

    /** Listas de entidades representados por este modelo. */
    private List<K> elementos;
    /** Lista de los valores que se muestran actualmente en el combobox. */
    private List<String> valores;
    /** Valor seleccionado. */
    private String valorSeleccionado;
    /** Propiedad que se recupera del objeto para mostrar en el combo box. */
    private Field propiedad;

    /**
     * Construye el modelo a partir de la clase de la entidad, obteniendo y
     * listando una de las propiedades de la lista de entidades.
     * @param clase clase de la entidad a modelar.
     * @param elementos lista de elementos que pertenecen al modelo.
     * @param campo propiedad que se visualiza en el combo box.
     */
    public EntityComboBoxModel( Class<K> clase, List<K> elementos, String campo ) {
        this( clase, campo );

        //setemos los elementos pasdas por parámetros
        setElementos( elementos );

    }//end constructor

    /**
     * Genera el modelo a partir de una clase y una propiedad, pero sin
     * elementos iniciales.
     *
     * @param clase clase de la entidad a modelar.
     * @param campo propiedad que se visualiza en el combo box.
     */
    public EntityComboBoxModel( Class<K> clase, String campo ) {
        super();

        //intentamos obtener la propiedad de la clase
        try {
            propiedad = clase.getDeclaredField( campo );

        } catch ( NoSuchFieldException nSFE ) {
            throw new IllegalArgumentException(
                    String.format( "La propiedad %s no existe en la clase %s.",
                                   campo, clase.getName() ) );

        }

        //seteamos la accesibilidad del campo para utilizarlo luego
        propiedad.setAccessible( true );

        //creamos la lista de elementos
        elementos = new LinkedList<K>();
        //creamos la lista de valores
        valores = new LinkedList<String>();

    }//end constructor

    /**
     * Devuelve los elementos contenidos en el modelo.
     * @return elementos del modelo.
     */
    public final List<K> getElementos() {
        return elementos;
        
    }//end método getElementos
    
    /**
     * Asigna los elementos del modelo, agregando ademas la propiedad
     * correspondiente de cada elemento a la interfáz gráfica.
     *
     * @param elementos elementos del modelo.
     */
    public final void setElementos( List<K> elementos ) {
        removeAllElements();

        int finalIndex = 0;

        if ( elementos != null && elementos.size() > 0 ) {
            this.elementos.addAll( elementos );

            finalIndex = elementos.size() - 1;

        }

        fireIntervalAdded( this, 0, finalIndex );

    }//end método setElementos      

    /**
     * Devuelve el elemento (el objeto, no la propiedad) que corresponde al
     * índice seleccionado.
     * @param index indice del que se desea obtener el elemento.
     * @return elemento en el índice seleccionado o null en caso de no existir
     * dicho elemento.
     */
    public K getElemento( int index ) {
        K elemento = null;

        if ( index >= 0 && index < elementos.size() )
            elemento = elementos.get( index );

        return elemento;
        
    }//end método getElemento
    
    /**
     * {@inheritDoc}
     */
    @Override
    public int getSize() {
        return elementos.size();

    }//end método getSize

    /**
     * {@inheritDoc}

     */
    @Override
    public Object getElementAt( int index ) {
        Object res = null;

        if ( index >= 0 && index < elementos.size() )
            try {
                res = propiedad.get( elementos.get( index ) );

            } catch ( Exception ex ) {
            }

        return res;

    }//end método getElementAt

    /**
     * {@inheritDoc}
     */
    @Override
    public void addElement( Object obj ) {
        try {
            valores.add( String.valueOf( propiedad.get( obj ) ) );
            elementos.add( (K)obj );
            fireIntervalAdded( this, elementos.size() - 1, 
                                     elementos.size() - 1 );
            
        } catch ( Exception ex ) {}

    }//end método addElement

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeElement( Object obj ) {
        int index = valores.indexOf( String.valueOf( obj ) );
        if ( index != -1 )
            removeElementAt( index );

    }//end método removeElement

    /**
     * {@inheritDoc}
     */
    @Override
    public void insertElementAt( Object obj, int index ) {
        try {
            valores.add( index, String.valueOf( propiedad.get( obj ) ) );
            elementos.add( index, (K)obj );

        } catch ( Exception ex ) {
        }

        fireIntervalAdded( this, index, index );

    }//end método insertElementAt

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeElementAt( int index ) {
        if ( getElementAt( index ) == valorSeleccionado )
            if ( index == 0 )
                setSelectedItem( getSize() == 1 ? null : getElementAt( index + 1 ) );
            else
                setSelectedItem( getElementAt( index - 1 ) );

        valores.remove( index );
        elementos.remove( index );

        fireIntervalRemoved( this, index, index );

    }//end método removeElementAt

    /**
     * {@inheritDoc}
     */
    @Override
    public void setSelectedItem( Object anItem ) {
        if ( ( valorSeleccionado != null && !valorSeleccionado.equals( 
               String.valueOf( anItem ) ) )
             || valorSeleccionado == null && anItem != null ) {
            valorSeleccionado = (String)anItem;
            fireContentsChanged( this, -1, -1 );

        }//end if

    }//end método setSelectedItem

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getSelectedItem() {
        return valorSeleccionado;

    }//end método getSelectedItem

    /**
     * Empties the list.
     */
    public void removeAllElements() {
        if ( elementos.size() > 0 ) {
            int firstIndex = 0;
            int lastIndex = elementos.size() - 1;
            elementos.clear();
            valores.clear();
            valorSeleccionado = null;
            fireIntervalRemoved( this, firstIndex, lastIndex );

        } else
            valorSeleccionado = null;

    }//end método removeAllElements

}//end clase EntityComboBoxModel

